Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document saturating behavior of addition and multiplication #13

Merged
merged 2 commits into from
Apr 9, 2018

Conversation

zyla
Copy link
Contributor

@zyla zyla commented Feb 22, 2018

Arithmetic operations on Int53 saturate on bottom and top. This may be non-obvious, since most in most languages integers wrap around instead. Instance documentation should clarify that.

@zyla
Copy link
Contributor Author

zyla commented Feb 22, 2018

Also fixes some bitrot in the build.

@rgrempel
Copy link
Owner

rgrempel commented Apr 5, 2018

Ah, yes, this is the result of the discussion here:

#8

So, the intention is to do what Purescript does for Int ... that is, to actually enforce top and bottom.

I suppose that if you enforce top and bottom, it's probably impossible to also completely obey the associativity laws ... so it's probably a question of which laws you'd prefer to break. I think it's probably impossible to obey associativity if you're using the Javascript number as your underlying implementation, since its behaviour past 53 bits for integers is necessarily going to be a bit weird. Or, to put it another way, if this is a 53-bit type, then behaviour past 53 bits is basically undefined.

I guess the other alternative would be to throw an exception past 53 bits ... but that's not what Int does past 32 bits, and my design goal is mostly to mimic what Int does.

Anyway, it probably makes sense to merge this documentation change ... I'm just thinking out loud again about the underlying behaviour, which I suppose still makes sense.

@rgrempel
Copy link
Owner

rgrempel commented Apr 5, 2018

Or, perhaps there is another behaviour in which you'd "wrap around" from top to bottom and vice versa? Perhaps it would be possible to obey the associativity laws that way, and still in a sense enforce top and bottom (though possibly in a surprising way).

Though I don't think that's what Int does.

@rgrempel
Copy link
Owner

rgrempel commented Apr 6, 2018

The "wrap around" behaviour is described here:

https://en.wikipedia.org/wiki/Integer_overflow

But I suppose what I really ought to do is re-verify what Int does, and make sure I'm mimicking it.

@zyla
Copy link
Contributor Author

zyla commented Apr 9, 2018

Or, perhaps there is another behaviour in which you'd "wrap around" from top to bottom and vice versa? Perhaps it would be possible to obey the associativity laws that way, and still in a sense enforce top and bottom (though possibly in a surprising way).

This is what most sane languages do. Haskell, for example:

λ> (maxBound :: Int) + 1 == minBound
True

And this obeys associativity of addition, and a few more laws that aren't satisfied by saturating arithmetic.

Though I don't think that's what Int does.

It does:

PSCi> (top :: Int) + 1 == bottom
true

Since we have to deal with JavaScript, it's not easy to implement, so I'm not sure it should be the behavior of Int53. But should at least be documented.

@zyla
Copy link
Contributor Author

zyla commented Apr 9, 2018

By the way, I found this issue while investigating jacobstanley/purescript-jack#25 . It's quite possible that the author of the RNG module expected Int53 to wrap around, since the module is a port from Haskell.

@rgrempel
Copy link
Owner

rgrempel commented Apr 9, 2018

Ah, oddly enough I ran into the same problem recently with a random number generator in purescript-elm-compat ... I broke it when saturating top and bottom in Int53, for (roughly) the same reason.

Basically, my design goals for Int53 are:

  • Mimic what Int does, but for 53 bits instead of 32; and
  • Have better performance than packages that work with arbitrary-length integers (since, otherwise, why not just use arbitrary length integers?)

So, if a "wrapping around" behaviour can be implemented in a way that doesn't eliminate the performance improvement vs. arbitrary length integers, that's what I'd prefer to do. (Speaking of which, I suppose I ought to benchmark the performance improvement if that's one of my design goals!)

Thinking out loud for a moment about implementation, I suppose the difficulty is that the "overflowed" number you get from Javascript past 53 bits is of limited use as an integer. You can compare it, so you can know you've gone over 53 bits. However, you've lost precision, so you can't directly use that number to "recover" a wrapped-around result.

So, I suppose you'd have to detect the overflowed situation (which the module does now), and then fall back on some other kind of calculation with the original arguments. As for what this would be, I can think of two things off the top of my head:

  • Do something clever. (E.g. if you've overflowed, represent the arguments as their difference from top (or bottom) and then do something with those representations to get the right answer).

  • Convert the arguments to arbitrary-length integers, do the calculation there, and then convert back to a wrapped-around Int53.

It seems to me that doing something clever might be possible for addition and subtraction, but I might be wrong about that. I'm less confident about multiplication or raising to a power.

Anyway, I'd like to give that a try -- I'd prefer not to saturate if Int doesn't do that.

@rgrempel
Copy link
Owner

rgrempel commented Apr 9, 2018

So, the Int type keeps things within 32 bits, by doing a bitwise shift of 0 bits, which in Javascript is 32-bit based.

This does a little bit more in Javascript than I had realized. For 32 bits, the first positive integer that overflows is 2147483648, and 2147483648 | 0 == -2147483648 ... the next is 2147483649 | 0 == -2147483647. So, you can see that a bitwise shift of 0 in Javascript is indeed wrapping around. Thinking about how the numbers are represented internally, I suppose I should have expected that.

Anyway, that just re-confirms that an ordinary Purescript Int calculation does wrap around -- I suppose, so long as it stays within 53 bits ... presumably after that strange things would start to happen.

@rgrempel
Copy link
Owner

rgrempel commented Apr 9, 2018

So, I've created #14 to think a bit about behaviour ... in the meantime, I suppose one should document what it does now. So, I'll merge this ... thanks!

@rgrempel rgrempel merged commit 2e3d634 into rgrempel:master Apr 9, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants